using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class playerCharacter : MonoBehaviour {
	
	public Level relatedLevel;
	public float speedX = 1.0f;
	
	public bool IsOnGround
    {
        get { return isOnGround; }
    }
	private bool isOnGround;
	
    private float movement;   
	private bool isJumping;
	private bool wasJumping;
    private float jumpTime;
	
    public bool IsAlive
    {
        get { return isAlive; }
    }
    bool isAlive;

    // Physics state
    public Vector2 Position
    {
		get { return new Vector2(transform.localPosition.x, transform.localPosition.y); }
		set
		{
			//Debug.Log("InputValue = " + value.ToString());
			Vector3 formerPosition = transform.localPosition;
			transform.localPosition = new Vector3(value.x , value.y, formerPosition.z);
		}
    	/*get { return position; }
        set
		{
			//Debug.Log("ApplyPosition called inputValue = " + value.ToString());
			position = value;
		}*/
    }
    Vector2 position;
	
	Vector2 previousPosition;
	
    private float previousBottom;

    public Vector2 Velocity
    {
        get { return velocity; }
        set { velocity = value; }
    }
    Vector2 velocity;
	
	public Rectangle BoundingRectangle
	{
		get { return new Rectangle(transform.localPosition.x, transform.localPosition.y, transform.localScale.x, transform.localScale.y); }
		/*get
		{
			//Debug.LogError(string.Format("BoundingRect = localPositionX {0} localPositionY {1} Position.x {2} position.y{3}",
			//transform.localPosition.x, transform.localPosition.y, Position.x, Position.y));
			return new Rectangle(Position.x, Position.y, transform.localScale.x, transform.localScale.y);
		}*/
	}
	
	private bool startedLoadLevel = false;
	
	    // Constants for controling horizontal movement
        private const float MoveAcceleration = 2400.0f;
        private const float MaxMoveSpeed = 350.0f;
        //private const float GroundDragFactor = 0.58f;
        //private const float AirDragFactor = 0.65f;
	    private const float GroundDragFactor = 0.80f;
        private const float AirDragFactor = 0.90f;
	
        // Constants for controlling vertical movement
        private const float MaxJumpTime = 0.55f;
        private const float JumpLaunchVelocity = 2000.0f;
	    //private const float MaxJumpTime = 1.00f;
        //private const float JumpLaunchVelocity = 1000.0f;
	
        private const float GravityAcceleration = -1800.0f;
        private const float MaxFallSpeed = 450.0f;
        private const float JumpControlPower = 0.14f;

        // Input configuration
        private const float MoveStickScale = 1.0f;		
		
	
	// Use this for initialization
	void Start()
	{
		Position = new Vector2(transform.localPosition.x, transform.localPosition.y);
		previousPosition = Position;
	}
	
	// Update is called once per frame
	void Update()
	{
		/*if(!relatedLevel.loadOver)
			return;
		
		Update(Time.deltaTime);
		GetInput();
		ApplyPhysics(Time.deltaTime);
		//ApplyPosition(Position);
		
		// Clear input.
        movement = 0.0f;
        isJumping = false;*/
	}
	
	void FixedUpdate()
	{
		//Debug.Log(PlayerInfo.Instance.score.ToString());
		//Debug.LogError("FixedUpdate called");
		
		if(!relatedLevel.loadOver)
			return;
		
		Update(Time.deltaTime);
		GetInput();
		ApplyPhysics(Time.deltaTime);
		//ApplyPosition(Position);
		
		HandleCameraMove();
		
		CheckLevelEvent();
		UpdateAudiouSource();
		
		// Clear input.
        movement = 0.0f;
        isJumping = false;
	}
	
	private void Update(float deltaTime)
	{
		/*if(Input.GetKey(KeyCode.LeftArrow))
		{
			Vector3 formerLocalPosition = gameObject.transform.localPosition;
			gameObject.transform.localPosition = new Vector3(formerLocalPosition.x - Time.deltaTime * speedX, formerLocalPosition.y, formerLocalPosition.z);
		}
		
		if(Input.GetKey(KeyCode.RightArrow))
		{
			Vector3 formerLocalPosition = gameObject.transform.localPosition;
			gameObject.transform.localPosition = new Vector3(formerLocalPosition.x + Time.deltaTime * speedX, formerLocalPosition.y, formerLocalPosition.z);	
		}*/
	}
	
	public void ApplyPosition(Vector2 inputValue)
	{
		Debug.LogError("ApplyPosition called inputValue = " + inputValue.ToString() + " formerValue = " + transform.localPosition.ToString());
		Vector3 formerPosition = transform.localPosition;
		transform.localPosition = new Vector3(inputValue.x , inputValue.y, formerPosition.z);		
	}
	
	public void ApplyPhysics(float deltaTime)
    {
        float elapsed = deltaTime;

        Vector2 previousPosition = Position;

        // Base velocity is a combination of horizontal movement control and
        // acceleration downward due to gravity.
        velocity.x += movement * MoveAcceleration * elapsed;
        velocity.y = MathHelper.Clamp(velocity.y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);

        velocity.y = DoJump(velocity.y, deltaTime);
		
		//Debug.LogError("velocity.y = " + velocity.y.ToString());
		
        // Apply pseudo-drag horizontally.
        if (IsOnGround)
            velocity.x *= GroundDragFactor;
        else
            velocity.x *= AirDragFactor;

        // Prevent the player from running faster than his top speed.            
        velocity.x = MathHelper.Clamp(velocity.x, -MaxMoveSpeed, MaxMoveSpeed);

        // Apply velocity.
		Rectangle lastBounding = BoundingRectangle;
        Position += velocity * elapsed;
        Position = new Vector2((float)Mathf.Round(Position.x), (float)Mathf.Round(Position.y));

        // If the player is now colliding with the level, separate them.
        HandleCollisions(lastBounding);
		
		if(!startedLoadLevel)
		{
			if(CheckExit())
			{
				Application.LoadLevel(relatedLevel.nextLevel);
				PlayerInfo.Instance.score += 100;
			}
		}
		
        // If the collision stopped us from moving, reset the velocity to zero.
        if (Position.x == previousPosition.x)
            velocity.x = 0;
		
		//Debug.Log(string.Format("Checking position.y {0} previousPosition.y {1}", Position.y, previousPosition.y));
        if (Position.y == previousPosition.y)
		{
            velocity.y = 0;
     	}
	}
	
	private float DoJump(float velocityY, float deltaTime)
    {
		//Debug.Log(string.Format("VelocityY {0} deltaTime {1}", velocity, deltaTime));
        // If the player wants to jump
        if (isJumping)
        {
			//Debug.LogError("IsJumping");
            // Begin or continue a jump
            if ((!wasJumping && IsOnGround) || jumpTime > 0.0f)
            {
                //if (jumpTime == 0.0f)
                //   jumpSound.Play();

                jumpTime += deltaTime;
                //sprite.PlayAnimation(jumpAnimation);
            }

            // If we are in the ascent of the jump
            if (0.0f < jumpTime && jumpTime <= MaxJumpTime)
            {
                // Fully override the vertical velocity with a power curve that gives players more control over the top of the jump
                velocityY = JumpLaunchVelocity * (1.0f - (float)Mathf.Pow(jumpTime / MaxJumpTime, JumpControlPower));
            }
            else
            {
                // Reached the apex of the jump
                jumpTime = 0.0f;
            }
        }
        else
        {
            // Continues not jumping or cancels a jump in progress
            jumpTime = 0.0f;
        }
        wasJumping = isJumping;

        return velocityY;
    }
	
	        
	private void GetInput()
    {
		if(Input.GetKey(KeyCode.LeftArrow))
		{
            movement = -1.0f;
        }
		if(Input.GetKey(KeyCode.RightArrow))
		{
           movement = 1.0f;
        }
		
		if(Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.Space))
		{
			//Debug.Log("Jump Btn Is Down");
			isJumping = true;
		}
		
    }
	
	
	public void ExternalCalledHandlerCollisions()
	{
		//HandleCollisions();
	}
	
	private bool CheckExit()
	{
		for(int i = 0; i < relatedLevel.exitPointList.Count; ++i)
		{
			Vector2 exitPoint = relatedLevel.exitPointList[i];
			Rectangle exitRect = relatedLevel.GetBounds((int)exitPoint.x, (int)exitPoint.y);
			Vector2 intersectionVector2 = RectangleExtensions.GetIntersectionDepth(BoundingRectangle, exitRect);
			
			/*Vector2 exitPoint = relatedLevel.exitPointList[i];
			Rectangle exitRect = relatedLevel.GetBounds((int)exitPoint.x, (int)exitPoint.y);
			//Debug.LogError(string.Format("X {0} Y {1}", exitRect.X, exitRect.Y));
			if(BoundingRectangle.ContainPoint(new Vector2(exitRect.X, exitRect.Y)))
				return true;*/
			if(intersectionVector2 != Vector2.zero)
				return true;
		}
		return false;
	}
	
	private void HandleCollisions(Rectangle lastRect)
	{
		
		Rectangle bounds = BoundingRectangle;
		//Debug.Log("bound = " + bounds.ToString());
        int leftTile = (int)Mathf.FloorToInt((float)(transform.localPosition.x - transform.localScale.x / 2) / (float)relatedLevel.tileWidth);
        int rightTile = (int)Mathf.CeilToInt((float)(transform.localPosition.x + transform.localScale.x / 2)/ (float)relatedLevel.tileWidth);
        int topTile = (int)Mathf.FloorToInt((float)(transform.localPosition.y + transform.localScale.y / 2) / (float)-relatedLevel.tileHeight);
        int bottomTile = (int)Mathf.CeilToInt((float)(transform.localPosition.y - transform.localScale.y / 2) / (float)-relatedLevel.tileHeight);
		
		//Debug.LogWarning(string.Format("transform.localPosition.y {0} transform.localScale.y / 2 {1} elatedLevel.tileHeight {2}",
		//	transform.localPosition.y, transform.localScale.y / 2, -relatedLevel.tileHeight));
		//Debug.LogWarning(string.Format("Left {0} Right {1} Top {2} Bottom {3}",
		//	leftTile, rightTile, topTile, bottomTile));
		
		// Reset flag to search for ground collision.
        isOnGround = false;

            // For each potentially colliding tile,
        for (int y = topTile; y <= bottomTile; ++y)
        {
            for (int x = leftTile; x <= rightTile; ++x)
            {
                // If this tile is collidable,
                TileCollision collision = relatedLevel.GetCollision(x, y);
				//Debug.Log(string.Format("GetCollision result = {0} X {1} Y {2}", collision, x, y));
                if (collision != TileCollision.Passable)
                {
                    // Determine collision depth (with direction) and magnitude.
                    Rectangle tileBounds = relatedLevel.GetBounds(x, y);
					//Debug.Log("bound = " + bounds.ToString());
					//Debug.Log("tileBounds = " + tileBounds.ToString());
                    Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                    
					//Segment movementSegment = new Segment(lastPosition, Position);
					Segment topRightMovementSegment = new Segment(lastRect.TopRightPt, bounds.TopRightPt);
					Segment topLeftMovementSegment = new Segment(lastRect.TopLeftPt, bounds.TopLeftPt);
					Segment bottomRightMovementSegment = new Segment(lastRect.BottomRightPt, bounds.BottomRightPt);
					Segment bottomLeftMovementSegment = new Segment(lastRect.BottomLeftPt, bounds.BottomLeftPt);
					bool collideWithLeftBorder = CollisionHelper.CheckTwoSegmentCrose(topRightMovementSegment, tileBounds.LeftBorder) || CollisionHelper.CheckTwoSegmentCrose(bottomRightMovementSegment, tileBounds.LeftBorder);
					bool collideWithBottomBorder = CollisionHelper.CheckTwoSegmentCrose(topRightMovementSegment, tileBounds.BottomBorder) ||  CollisionHelper.CheckTwoSegmentCrose(topLeftMovementSegment, tileBounds.BottomBorder);
					bool collideWithRightBorder = CollisionHelper.CheckTwoSegmentCrose(topLeftMovementSegment, tileBounds.RightBorder) || CollisionHelper.CheckTwoSegmentCrose(bottomLeftMovementSegment, tileBounds.RightBorder);
					bool collideWithTopBorder = CollisionHelper.CheckTwoSegmentCrose(bottomRightMovementSegment, tileBounds.TopBorder) || CollisionHelper.CheckTwoSegmentCrose(bottomLeftMovementSegment, tileBounds.TopBorder);
					
					//Debug.Log("depth = " + depth.ToString());
					
					if (depth != Vector2.zero)
                    {
                        float absDepthX = Mathf.Abs(depth.x);
                        float absDepthY = Mathf.Abs(depth.y);
                        // Resolve the collision along the shallow axis.
						
						if (previousBottom <= tileBounds.Top)
						{
							//Debug.Log("Set IsOnGround to true");
                            isOnGround = true;
						}
						
                        if ((collideWithBottomBorder || collideWithTopBorder) && !(collideWithLeftBorder || collideWithRightBorder))
                        {
							//Debug.Log("Collide Top/Bottom");
                            // If we crossed the top of a tile, we are on the ground.
							
                            // Ignore platforms, unless we are on the ground.
                            if (collision == TileCollision.Impassable || IsOnGround)
                            {
                                // Resolve the collision along the Y axis.
                                //Position = new Vector2(Position.X, Position.Y + depth.Y);
								Vector3 formerPosition = transform.localPosition;
								//Debug.LogError("Before: transform.localPosition =" + transform.localPosition .ToString());
								transform.localPosition = new Vector3(formerPosition.x, formerPosition.y + depth.y, formerPosition.z);
								//Debug.LogError("After: transform.localPosition =" + transform.localPosition .ToString());
                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
								
                            }
                        }
                        else if (!(collideWithBottomBorder || collideWithTopBorder) && (collideWithLeftBorder || collideWithRightBorder))
						{
							Debug.Log("Collide Left/Right");
                            // Resolve the collision along the X axis.
							Vector3 formerPosition = transform.localPosition;
							//Debug.LogError("Before: transform.localPosition =" + transform.localPosition .ToString());
							transform.localPosition = new Vector3(formerPosition.x + depth.x, formerPosition.y, formerPosition.z);
							//Debug.LogError("After: transform.localPosition =" + transform.localPosition .ToString());
                            // Perform further collisions with the new bounds.
                            bounds = BoundingRectangle;
						}
						else if((collideWithBottomBorder || collideWithTopBorder) && (collideWithLeftBorder || collideWithRightBorder))
						{
							Debug.Log("collide with block in both horizontal and Vertical direction --> checking the direction of the block");
							
							if(relatedLevel.isHorizontalBlock(x, y))
							{
								//Debug.LogError(string.Format("x {0}, y {1} is Horizontal",x ,y));
								transform.localPosition = new Vector3(transform.localPosition.x,
									transform.localPosition.y + depth.y, transform.localPosition.z);
							}
							if(relatedLevel.isVerticalBlock(x, y))
							{
								//Debug.LogError(string.Format("x {0}, y {1} is Vertical",x ,y));
								transform.localPosition = new Vector3(transform.localPosition.x + depth.x,
									transform.localPosition.y, transform.localPosition.z);
							}
							
							bounds = BoundingRectangle;
								
						}
						
						if(depth.y < 0)
						{
							//Debug.LogError("Stop Jumping");
							jumpTime = 100.0f;	
						}
					}
					
					
				}
			}
		}

		previousBottom = bounds.Bottom;
	}
	
	private void CheckLevelEvent()
	{
		List<LevelEvent> levelEventList = relatedLevel.levelEventList;
		for(int i = 0; i < levelEventList.Count; ++i)
		{
			if(BoundingRectangle.InteractWithCircle(levelEventList[i].centerPosition, levelEventList[i].radius))
			{
				levelEventList[i].TriggerEvent();
			}
		}
	}
	
	private void UpdateAudiouSource()
	{
		relatedLevel.UpdateAudiouSource(Position);
	}
	
	private void HandleCameraMove()
	{
		//if(Position.y < -400 || Position.y > -200)
		relatedLevel.transform.localPosition += new Vector3(0, previousPosition.y - Position.y, 0);
		
		if(Position.x < 200 || Position.x > 600)
			relatedLevel.transform.localPosition += new Vector3(previousPosition.x - Position.x, 0, 0);
		
		previousPosition = Position;
	}
}
